package com.minimalist.common.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.minimalist.common.config.exception.BusinessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * Excel生成工具类
 * 基于EasyExcel实现Excel的读取、写入和模板填充功能
 * 支持大数据量处理、异常处理和资源管理
 * 
 * 主要功能：
 * 1. 基础读写：支持Excel文件和流的读取写入
 * 2. 模板填充：支持简单和复杂模板填充
 * 3. 批量处理：支持大数据量分批处理
 * 4. 多工作表：支持多个工作表的读写操作
 * 5. 异常处理：完善的异常处理和日志记录
 * 6. 资源管理：自动资源释放和清理
 *
 * @author 11987
 * @version 2.0
 */
public class ExcelGenerate{

    private static final Logger logger = LoggerFactory.getLogger(ExcelGenerate.class);
    
    /** 默认批处理大小 */
    public static final int DEFAULT_BATCH_SIZE = 1000;
    
    /** 支持的Excel文件扩展名 */
    private static final String[] SUPPORTED_EXTENSIONS = {".xlsx", ".xls"};

    /**
     * 写入Excel文件
     *
     * @param fileName  文件名
     * @param sheetName 工作表名
     * @param clazz     数据类型
     * @param data      数据列表
     * @param <T>       泛型类型
     * @throws BusinessException 写入失败时抛出异常
     */
    public static <T> void writeExcel(String fileName, String sheetName
            , Class<T> clazz, List<T> data){
        try {
            logger.info("开始写入Excel文件: {}, 工作表: {}, 数据量: {}", fileName, sheetName, data != null ? data.size() : 0);
            EasyExcel.write(fileName, clazz)
                    .sheet(sheetName)
                    .doWrite(data);
            logger.info("Excel文件写入完成: {}", fileName);
        } catch (Exception e) {
            logger.error("写入Excel文件失败: {}", fileName, e);
            throw new BusinessException("写入Excel文件失败: " + e.getMessage());
        }
    }

    /**
     * 写入Excel文件到输出流
     *
     * @param outputStream 输出流
     * @param sheetName    工作表名
     * @param clazz        数据类型
     * @param data         数据列表
     * @param <T>          泛型类型
     * @throws BusinessException 写入失败时抛出异常
     */
    public static <T> void writeExcel(OutputStream outputStream
            , String sheetName, Class<T> clazz, List<T> data){
        try {
            logger.info("开始写入Excel到输出流, 工作表: {}, 数据量: {}", sheetName, data != null ? data.size() : 0);
            EasyExcel.write(outputStream, clazz)
                    .sheet(sheetName)
                    .doWrite(data);
            logger.info("Excel输出流写入完成");
        } catch (Exception e) {
            logger.error("写入Excel到输出流失败", e);
            throw new BusinessException("写入Excel到输出流失败: " + e.getMessage());
        }
    }

    /**
     * 写入多个工作表的Excel文件
     *
     * @param fileName   文件名
     * @param sheetDatas 工作表数据映射，key为工作表名，value为数据和类型的包装
     */
    public static void writeMultiSheetExcel(String fileName
            , Map<String, SheetData<?>> sheetDatas){
        ExcelWriterBuilder writerBuilder = EasyExcel.write(fileName);
        try (var excelWriter = writerBuilder.build()){
            int sheetNo = 0;
            for (Map.Entry<String, SheetData<?>> entry : sheetDatas.entrySet()){
                String sheetName = entry.getKey();
                SheetData<?> sheetData = entry.getValue();
                WriteSheet writeSheet = EasyExcel.writerSheet(sheetNo++, sheetName)
                        .head(sheetData.getClazz())
                        .build();
                excelWriter.write(sheetData.getData(), writeSheet);
            }
        }
    }

    /**
     * 读取Excel文件
     *
     * @param fileName 文件名
     * @param clazz    数据类型
     * @param listener 读取监听器
     * @param <T>      泛型类型
     * @throws BusinessException 读取失败时抛出异常
     */
    public static <T> void readExcel(String fileName, Class<T> clazz
            , ReadListener<T> listener){
        try {
            logger.info("开始读取Excel文件: {}", fileName);
            // 验证文件格式和内容
            validateExcelFileContent(fileName);
            EasyExcel.read(fileName, clazz, listener)
                    .sheet()
                    .doRead();
            logger.info("Excel文件读取完成: {}", fileName);
        } catch (Exception e) {
            logger.error("读取Excel文件失败: {}", fileName, e);
            throw new BusinessException("读取Excel文件失败: " + e.getMessage());
        }
    }

    /**
     * 读取Excel文件从输入流
     *
     * @param inputStream 输入流
     * @param clazz       数据类型
     * @param listener    读取监听器
     * @param <T>         泛型类型
     * @throws BusinessException 读取失败时抛出异常
     */
    public static <T> void readExcel(InputStream inputStream, Class<T> clazz
            , ReadListener<T> listener){
        try {
            logger.info("开始从输入流读取Excel");
            // 创建支持标记的输入流
            InputStream markableStream = createMarkableInputStream(inputStream);
            // 注意：由于无法确定文件扩展名，跳过魔数验证，依赖EasyExcel的内部验证
            EasyExcel.read(markableStream, clazz, listener)
                    .sheet()
                    .doRead();
            logger.info("从输入流读取Excel完成");
        } catch (Exception e) {
            logger.error("从输入流读取Excel失败", e);
            // 检查是否为文件格式错误
            if (e.getCause() instanceof org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException ||
                e.getMessage().contains("NotOfficeXmlFileException") ||
                e.getMessage().contains("Unexpected record signature")) {
                throw new BusinessException("Excel文件格式无效或文件损坏，请检查文件是否为有效的Excel文件");
            }
            throw new BusinessException("从输入流读取Excel失败: " + e.getMessage());
        }
    }

    /**
     * 根据模板填充数据生成Excel文件
     *
     * @param templateFileName 模板文件名
     * @param fileName         输出文件名
     * @param data             填充数据
     * @throws BusinessException 填充失败时抛出异常
     */
    public static void fillTemplate(String templateFileName, String fileName, Object data){
        try {
            logger.info("开始使用模板填充Excel: 模板={}, 输出={}", templateFileName, fileName);
            // 验证模板文件格式和内容
            validateExcelFileContent(templateFileName);
            EasyExcel.write(fileName)
                    .withTemplate(templateFileName)
                    .sheet()
                    .doFill(data);
            logger.info("模板填充完成: {}", fileName);
        } catch (Exception e) {
            logger.error("模板填充失败: 模板={}, 输出={}", templateFileName, fileName, e);
            throw new BusinessException("模板填充失败: " + e.getMessage());
        }
    }

    /**
     * 根据模板填充数据生成Excel到输出流
     *
     * @param templateInputStream 模板输入流
     * @param outputStream        输出流
     * @param data                填充数据
     * @throws BusinessException 填充失败时抛出异常
     */
    public static void fillTemplate(InputStream templateInputStream
            , OutputStream outputStream, Object data){
        try {
            logger.info("开始使用模板填充Excel到输出流");
            // 创建支持标记的输入流
            InputStream markableStream = createMarkableInputStream(templateInputStream);
            EasyExcel.write(outputStream)
                    .withTemplate(markableStream)
                    .sheet()
                    .doFill(data);
            logger.info("模板填充到输出流完成");
        } catch (Exception e) {
            logger.error("模板填充到输出流失败", e);
            // 检查是否为文件格式错误
            if (e.getCause() instanceof org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException ||
                e.getMessage().contains("NotOfficeXmlFileException") ||
                e.getMessage().contains("Unexpected record signature")) {
                throw new BusinessException("模板文件格式无效或文件损坏，请检查模板文件是否为有效的Excel文件");
            }
            throw new BusinessException("模板填充到输出流失败: " + e.getMessage());
        }
    }

    /**
     * 根据模板填充多组数据生成Excel文件
     *
     * @param templateFileName 模板文件名
     * @param fileName         输出文件名
     * @param data             填充数据映射
     */
    public static void fillTemplateWithMultipleData(String templateFileName
            , String fileName, Map<String, Object> data){
        try (var excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()){
            WriteSheet writeSheet = EasyExcel.writerSheet().build();

            for (Map.Entry<String, Object> entry : data.entrySet()){
                if (entry.getValue() instanceof Collection){
                    // 列表数据填充
                    excelWriter.fill(entry.getValue(), writeSheet);
                } else{
                    // 单个数据填充
                    excelWriter.fill(entry.getValue(), writeSheet);
                }
            }
        }
    }

    /**
     * 根据模板填充复杂数据结构
     *
     * @param templateInputStream 模板输入流
     * @param outputStream        输出流
     * @param fillData            填充数据
     * @param listData            列表数据
     * @throws BusinessException 填充失败时抛出异常
     */
    public static void fillComplexTemplate(InputStream templateInputStream
            , OutputStream outputStream, Object fillData, List<?> listData){
        try {
            logger.info("开始复杂模板填充到输出流, 列表数据量={}", 
                    listData != null ? listData.size() : 0);
            
            // 创建支持标记的输入流
            InputStream markableStream = createMarkableInputStream(templateInputStream);
            
            try (var excelWriter = EasyExcel.write(outputStream).withTemplate(markableStream).build()){
                WriteSheet writeSheet = EasyExcel.writerSheet().build();

                // 填充单个数据
                if (fillData != null){
                    excelWriter.fill(fillData, writeSheet);
                }

                // 填充列表数据
                if (listData != null && !listData.isEmpty()){
                    FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
                    excelWriter.fill(new FillWrapper("list", listData), fillConfig, writeSheet);
                }
            }
            
            logger.info("复杂模板填充到输出流完成");
        } catch (Exception e) {
            logger.error("复杂模板填充到输出流失败", e);
            // 检查是否为文件格式错误
            if (e.getCause() instanceof org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException ||
                e.getMessage().contains("NotOfficeXmlFileException") ||
                e.getMessage().contains("Unexpected record signature")) {
                throw new BusinessException("模板文件格式无效或文件损坏，请检查模板文件是否为有效的Excel文件");
            }
            throw new BusinessException("复杂模板填充到输出流失败: " + e.getMessage());
        }
    }

    /**
     * 根据模板填充复杂数据结构(Map数据)
     *
     * @param templateInputStream 模板输入流
     * @param outputStream        输出流
     * @param fillData            填充数据映射
     * @param listData            列表数据
     * @throws BusinessException 填充失败时抛出异常
     */
    public static void fillComplexTemplate(InputStream templateInputStream
            , OutputStream outputStream, Map<String, Object> fillData, List<?> listData){
        try {
            logger.info("开始复杂模板填充到输出流(Map数据), 列表数据量={}", 
                    listData != null ? listData.size() : 0);
            
            // 创建支持标记的输入流
            InputStream markableStream = createMarkableInputStream(templateInputStream);
            
            try (var excelWriter = EasyExcel.write(outputStream).withTemplate(markableStream).build()){
                WriteSheet writeSheet = EasyExcel.writerSheet().build();

                // 填充Map数据
                if (fillData != null && !fillData.isEmpty()){
                    excelWriter.fill(fillData, writeSheet);
                }

                // 填充列表数据
                if (listData != null && !listData.isEmpty()){
                    FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
                    excelWriter.fill(new FillWrapper("list", listData), fillConfig, writeSheet);
                }
            }
            
            logger.info("复杂模板填充到输出流(Map数据)完成");
        } catch (Exception e) {
            logger.error("复杂模板填充到输出流(Map数据)失败", e);
            // 检查是否为文件格式错误
            if (e.getCause() instanceof org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException ||
                e.getMessage().contains("NotOfficeXmlFileException") ||
                e.getMessage().contains("Unexpected record signature")) {
                throw new BusinessException("模板文件格式无效或文件损坏，请检查模板文件是否为有效的Excel文件");
            }
            throw new BusinessException("复杂模板填充到输出流(Map数据)失败: " + e.getMessage());
        }
    }

    /**
     * 根据模板填充复杂数据结构到文件
     *
     * @param templateFileName 模板文件名
     * @param fileName         输出文件名
     * @param fillData         填充数据
     * @param listData         列表数据
     * @throws BusinessException 填充失败时抛出异常
     */
    public static void fillComplexTemplate(String templateFileName, String fileName
            , Object fillData, List<?> listData){
        try {
            logger.info("开始复杂模板填充: 模板={}, 输出={}, 列表数据量={}", 
                    templateFileName, fileName, listData != null ? listData.size() : 0);
            
            // 验证模板文件格式和内容
            validateExcelFileContent(templateFileName);
            
            try (var excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()){
                WriteSheet writeSheet = EasyExcel.writerSheet().build();

                // 填充单个数据
                if (fillData != null){
                    excelWriter.fill(fillData, writeSheet);
                }

                // 填充列表数据
                if (listData != null && !listData.isEmpty()){
                    FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
                    excelWriter.fill(new FillWrapper("list", listData), fillConfig, writeSheet);
                }
            }
            
            logger.info("复杂模板填充完成: {}", fileName);
        } catch (Exception e) {
            logger.error("复杂模板填充失败: 模板={}, 输出={}", templateFileName, fileName, e);
            throw new BusinessException("复杂模板填充失败: " + e.getMessage());
        }
    }

    /**
     * 工作表数据包装类
     *
     * @param <T> 数据类型
     */
    public static class SheetData<T>{
        private final Class<T> clazz;
        private final List<T> data;

        public SheetData(Class<T> clazz, List<T> data){
            this.clazz = clazz;
            this.data = data;
        }

        public Class<T> getClazz(){
            return clazz;
        }

        public List<T> getData(){
            return data;
        }
    }

    /**
     * 创建工作表数据
     *
     * @param clazz 数据类型
     * @param data  数据列表
     * @param <T>   泛型类型
     * @return 工作表数据
     */
    public static <T> SheetData<T> createSheetData(Class<T> clazz, List<T> data){
        return new SheetData<>(clazz, data);
    }

    /**
     * 验证文件扩展名是否为Excel格式
     *
     * @param fileName 文件名
     * @return 是否为Excel格式
     */
    public static boolean isExcelFile(String fileName){
        if (!StringUtils.hasText(fileName)){
            return false;
        }
        String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
        return "xls".equals(extension) || "xlsx".equals(extension);
    }

    /**
     * 验证Excel文件是否有效
     * 检查文件是否存在、格式是否正确、内容是否有效
     *
     * @param fileName 文件名
     * @throws BusinessException 文件无效时抛出异常
     */
    public static void validateExcelFileContent(String fileName) {
        if (!StringUtils.hasText(fileName)) {
            throw new BusinessException("文件名不能为空");
        }
        
        File file = new File(fileName);
        if (!file.exists()) {
            throw new BusinessException("文件不存在: " + fileName);
        }
        
        if (!isExcelFile(fileName)) {
            throw new BusinessException("不支持的文件格式，仅支持.xlsx和.xls格式");
        }
        
        if (file.length() == 0) {
            throw new BusinessException("文件为空: " + fileName);
        }
        
        // 尝试读取文件头部验证是否为有效的Excel文件
        try (InputStream inputStream = new java.io.FileInputStream(file)) {
            byte[] header = new byte[8];
            int bytesRead = inputStream.read(header);
            
            if (bytesRead < 8) {
                throw new BusinessException("文件内容不完整或损坏: " + fileName);
            }
            
            // 检查Excel文件的魔数
            if (fileName.toLowerCase().endsWith(".xlsx")) {
                // XLSX文件应该以PK开头（ZIP格式）
                if (header[0] != 0x50 || header[1] != 0x4B) {
                    throw new BusinessException("XLSX文件格式无效或文件损坏: " + fileName);
                }
            } else if (fileName.toLowerCase().endsWith(".xls")) {
                // XLS文件的OLE2格式魔数
                if (header[0] != (byte)0xD0 || header[1] != (byte)0xCF || 
                    header[2] != (byte)0x11 || header[3] != (byte)0xE0) {
                    throw new BusinessException("XLS文件格式无效或文件损坏: " + fileName);
                }
            }
        } catch (java.io.IOException e) {
            throw new BusinessException("读取文件时发生错误: " + e.getMessage());
        }
    }
    
    /**
     * 验证输入流中的Excel内容是否有效
     *
     * @param inputStream 输入流
     * @param fileName 文件名（用于确定格式）
     * @throws BusinessException 内容无效时抛出异常
     */
    public static void validateExcelStreamContent(InputStream inputStream, String fileName) {
        if (inputStream == null) {
            throw new BusinessException("输入流不能为空");
        }
        
        if (!StringUtils.hasText(fileName) || !isExcelFile(fileName)) {
            throw new BusinessException("不支持的文件格式，仅支持.xlsx和.xls格式");
        }
        
        try {
            // 标记输入流以便重置
            if (!inputStream.markSupported()) {
                // 如果输入流不支持标记，则包装为BufferedInputStream
                logger.warn("输入流不支持标记操作，将包装为BufferedInputStream进行验证");
                return;
            }
            
            inputStream.mark(8);
            byte[] header = new byte[8];
            int bytesRead = inputStream.read(header);
            inputStream.reset();
            
            if (bytesRead < 8) {
                throw new BusinessException("输入流内容不完整或损坏");
            }
            
            // 检查Excel文件的魔数
            if (fileName.toLowerCase().endsWith(".xlsx")) {
                // XLSX文件应该以PK开头（ZIP格式）
                if (header[0] != 0x50 || header[1] != 0x4B) {
                    throw new BusinessException("XLSX文件格式无效或内容损坏");
                }
            } else if (fileName.toLowerCase().endsWith(".xls")) {
                // XLS文件的OLE2格式魔数
                if (header[0] != (byte)0xD0 || header[1] != (byte)0xCF || 
                    header[2] != (byte)0x11 || header[3] != (byte)0xE0) {
                    throw new BusinessException("XLS文件格式无效或内容损坏");
                }
            }
        } catch (java.io.IOException e) {
            throw new BusinessException("验证输入流时发生错误: " + e.getMessage());
        }
    }
    
    /**
     * 创建支持标记的输入流包装器
     * 如果原始输入流不支持标记，则包装为BufferedInputStream
     *
     * @param inputStream 原始输入流
     * @return 支持标记的输入流
     */
    private static InputStream createMarkableInputStream(InputStream inputStream) {
        if (inputStream.markSupported()) {
            return inputStream;
        }
        return new java.io.BufferedInputStream(inputStream);
    }

    /**
     * 写入多个工作表的Excel文件
     *
     * @param fileName   文件名
     * @param sheetDataMap 工作表数据映射（工作表名 -> 数据列表）
     * @param clazz      数据类型
     * @param <T>        泛型类型
     * @throws BusinessException 写入失败时抛出异常
     */
    public static <T> void writeMultiSheetExcel(String fileName, Map<String, List<T>> sheetDataMap, Class<T> clazz) {
        ExcelWriter excelWriter = null;
        try {
            logger.info("开始写入多工作表Excel文件: {}, 工作表数量: {}", fileName, sheetDataMap.size());
            excelWriter = EasyExcel.write(fileName, clazz).build();
            
            int sheetIndex = 0;
            for (Map.Entry<String, List<T>> entry : sheetDataMap.entrySet()) {
                String sheetName = entry.getKey();
                List<T> data = entry.getValue();
                WriteSheet writeSheet = EasyExcel.writerSheet(sheetIndex++, sheetName).build();
                excelWriter.write(data, writeSheet);
                logger.debug("工作表 {} 写入完成，数据量: {}", sheetName, data != null ? data.size() : 0);
            }
            
            logger.info("多工作表Excel文件写入完成: {}", fileName);
        } catch (Exception e) {
            logger.error("写入多工作表Excel文件失败: {}", fileName, e);
            throw new BusinessException("写入多工作表Excel文件失败: " + e.getMessage());
        } finally {
            if (excelWriter != null) {
                try {
                    excelWriter.finish();
                } catch (Exception e) {
                    logger.warn("关闭ExcelWriter时发生异常", e);
                }
            }
        }
    }

    /**
     * 读取Excel文件的所有工作表
     *
     * @param fileName 文件名
     * @param clazz    数据类型
     * @param <T>      泛型类型
     * @return 工作表数据映射（工作表名 -> 数据列表）
     * @throws BusinessException 读取失败时抛出异常
     */
    public static <T> Map<String, List<T>> readAllSheets(String fileName, Class<T> clazz) {
        Map<String, List<T>> result = new HashMap<>();
        ExcelReader excelReader = null;
        try {
            logger.info("开始读取Excel所有工作表: {}", fileName);
            // 验证文件格式和内容
            validateExcelFileContent(fileName);
            excelReader = EasyExcel.read(fileName).build();
            List<ReadSheet> readSheets = excelReader.excelExecutor().sheetList();
            
            for (ReadSheet readSheet : readSheets) {
                String sheetName = readSheet.getSheetName();
                List<T> dataList = new ArrayList<>();
                
                // 为每个工作表创建一个读取监听器
                ReadSheet configuredSheet = EasyExcel.readSheet(readSheet.getSheetNo())
                        .head(clazz)
                        .registerReadListener(new SimpleDataListener<>(dataList))
                        .build();
                
                excelReader.read(configuredSheet);
                result.put(sheetName, dataList);
                logger.debug("工作表 {} 读取完成，数据量: {}", sheetName, dataList.size());
            }
            
            logger.info("Excel所有工作表读取完成: {}, 共 {} 个工作表", fileName, result.size());
            return result;
        } catch (Exception e) {
            logger.error("读取Excel所有工作表失败: {}", fileName, e);
            throw new BusinessException("读取Excel所有工作表失败: " + e.getMessage());
        } finally {
            if (excelReader != null) {
                try {
                    excelReader.finish();
                } catch (Exception e) {
                    logger.warn("关闭ExcelReader时发生异常", e);
                }
            }
        }
    }

    /**
     * 获取文件扩展名
     *
     * @param fileName 文件名
     * @return 扩展名
     */
    public static String getFileExtension(String fileName){
        if (!StringUtils.hasText(fileName)){
            return "";
        }
        int lastDotIndex = fileName.lastIndexOf(".");
        return lastDotIndex > 0 ? fileName.substring(lastDotIndex + 1).toLowerCase() : "";
    }

    /**
     * 批量写入Excel（支持大数据量分批写入）
     *
     * @param fileName  文件名
     * @param sheetName 工作表名
     * @param clazz     数据类型
     * @param dataSupplier 数据供应器，支持分批获取数据
     * @param batchSize 每批处理的数据量
     * @param <T>       泛型类型
     * @throws BusinessException 写入失败时抛出异常
     */
    public static <T> void writeBatchExcel(String fileName, String sheetName,
                                           Class<T> clazz, Supplier<List<T>> dataSupplier, int batchSize) {
        ExcelWriter excelWriter = null;
        try {
            logger.info("开始批量写入Excel文件: {}, 工作表: {}, 批次大小: {}", fileName, sheetName, batchSize);
            excelWriter = EasyExcel.write(fileName, clazz).build();
            WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
            
            int totalCount = 0;
            List<T> batchData;
            while ((batchData = dataSupplier.get()) != null && !batchData.isEmpty()) {
                excelWriter.write(batchData, writeSheet);
                totalCount += batchData.size();
                logger.debug("已写入 {} 条数据", totalCount);
            }
            
            logger.info("批量写入Excel完成: {}, 总计写入 {} 条数据", fileName, totalCount);
        } catch (Exception e) {
            logger.error("批量写入Excel失败: {}", fileName, e);
            throw new BusinessException("批量写入Excel失败: " + e.getMessage());
        } finally {
            if (excelWriter != null) {
                try {
                    excelWriter.finish();
                } catch (Exception e) {
                    logger.warn("关闭ExcelWriter时发生异常", e);
                }
            }
        }
    }

    /**
     * 读取Excel文件并返回数据列表（适用于小文件）
     *
     * @param fileName 文件名
     * @param clazz    数据类型
     * @param <T>      泛型类型
     * @return 数据列表
     * @throws BusinessException 读取失败时抛出异常
     */
    public static <T> List<T> readExcelToList(String fileName, Class<T> clazz) {
        List<T> dataList = new ArrayList<>();
        try {
            logger.info("开始读取Excel文件到列表: {}", fileName);
            // 验证文件格式和内容
            validateExcelFileContent(fileName);
            EasyExcel.read(fileName, clazz, new SimpleDataListener<>(dataList))
                    .sheet()
                    .doRead();
            logger.info("读取Excel完成: {}, 共读取 {} 条数据", fileName, dataList.size());
            return dataList;
        } catch (Exception e) {
            logger.error("读取Excel文件到列表失败: {}", fileName, e);
            throw new BusinessException("读取Excel到列表失败: " + e.getMessage());
        }
    }

    /**
     * 读取Excel文件指定工作表
     *
     * @param fileName  文件名
     * @param sheetName 工作表名
     * @param clazz     数据类型
     * @param listener  读取监听器
     * @param <T>       泛型类型
     * @throws BusinessException 读取失败时抛出异常
     */
    public static <T> void readExcelSheet(String fileName, String sheetName,
                                          Class<T> clazz, ReadListener<T> listener) {
        try {
            logger.info("开始读取Excel工作表: 文件={}, 工作表={}", fileName, sheetName);
            // 验证文件格式和内容
            validateExcelFileContent(fileName);
            EasyExcel.read(fileName, clazz, listener)
                    .sheet(sheetName)
                    .doRead();
            logger.info("读取Excel工作表完成: 文件={}, 工作表={}", fileName, sheetName);
        } catch (Exception e) {
            logger.error("读取Excel工作表失败: 文件={}, 工作表={}", fileName, sheetName, e);
            throw new BusinessException("读取Excel指定工作表失败: " + e.getMessage());
        }
    }

    /**
     * 验证Excel文件是否存在且可读
     *
     * @param fileName 文件名
     * @return 是否有效
     */
    public static boolean validateExcelFile(String fileName) {
        if (!StringUtils.hasText(fileName)) {
            logger.warn("文件名为空");
            return false;
        }
        
        File file = new File(fileName);
        if (!file.exists()) {
            logger.warn("文件不存在: {}", fileName);
            return false;
        }
        
        if (!file.canRead()) {
            logger.warn("文件不可读: {}", fileName);
            return false;
        }
        
        String extension = fileName.toLowerCase();
        boolean isSupported = false;
        for (String supportedExt : SUPPORTED_EXTENSIONS) {
            if (extension.endsWith(supportedExt)) {
                isSupported = true;
                break;
            }
        }
        
        if (!isSupported) {
            logger.warn("不支持的文件格式: {}", fileName);
            return false;
        }
        
        return true;
    }

    /**
     * 获取Excel文件的工作表信息
     *
     * @param fileName 文件名
     * @return 工作表信息列表（包含工作表名称和索引）
     * @throws BusinessException 读取失败时抛出异常
     */
    public static List<Map<String, Object>> getSheetInfo(String fileName) {
        List<Map<String, Object>> sheetInfoList = new ArrayList<>();
        ExcelReader excelReader = null;
        try {
            logger.info("开始获取Excel工作表信息: {}", fileName);
            // 验证文件格式和内容
            validateExcelFileContent(fileName);
            excelReader = EasyExcel.read(fileName).build();
            List<ReadSheet> readSheets = excelReader.excelExecutor().sheetList();
            
            for (ReadSheet readSheet : readSheets) {
                Map<String, Object> sheetInfo = new HashMap<>();
                sheetInfo.put("sheetNo", readSheet.getSheetNo());
                sheetInfo.put("sheetName", readSheet.getSheetName());
                sheetInfoList.add(sheetInfo);
            }
            
            logger.info("获取Excel工作表信息完成: {}, 共 {} 个工作表", fileName, sheetInfoList.size());
            return sheetInfoList;
        } catch (Exception e) {
            logger.error("获取Excel工作表信息失败: {}", fileName, e);
            throw new BusinessException("获取Excel工作表信息失败: " + e.getMessage());
        } finally {
            if (excelReader != null) {
                try {
                    excelReader.finish();
                } catch (Exception e) {
                    logger.warn("关闭ExcelReader时发生异常", e);
                }
            }
        }
    }

    /**
     * 分页读取Excel数据（适用于大文件）
     *
     * @param fileName   文件名
     * @param clazz      数据类型
     * @param pageSize   每页大小
     * @param pageNum    页码（从1开始）
     * @param <T>        泛型类型
     * @return 分页数据
     * @throws BusinessException 读取失败时抛出异常
     */
    public static <T> List<T> readExcelByPage(String fileName, Class<T> clazz, int pageSize, int pageNum) {
        List<T> dataList = new ArrayList<>();
        try {
            logger.info("开始分页读取Excel: 文件={}, 页大小={}, 页码={}", fileName, pageSize, pageNum);
            // 验证文件格式和内容
            validateExcelFileContent(fileName);
            
            // 计算跳过的行数
            int skipRows = (pageNum - 1) * pageSize;
            
            EasyExcel.read(fileName, clazz, new PageDataListener<>(dataList, skipRows, pageSize))
                    .sheet()
                    .doRead();
            
            logger.info("分页读取Excel完成: 文件={}, 实际读取 {} 条数据", fileName, dataList.size());
            return dataList;
        } catch (Exception e) {
            logger.error("分页读取Excel失败: 文件={}", fileName, e);
            throw new BusinessException("分页读取Excel失败: " + e.getMessage());
        }
    }

    /**
     * 数据读取监听器
     * 用于处理Excel读取过程中的数据
     *
     * @param <T> 数据类型
     */
    public static class DataListener<T> extends AnalysisEventListener<T> {
        private List<T> dataList = new ArrayList<>();
        private final int batchSize;
        private final Consumer<List<T>> batchProcessor;

        public DataListener(int batchSize, Consumer<List<T>> batchProcessor) {
            this.batchSize = batchSize;
            this.batchProcessor = batchProcessor;
        }

        @Override
        public void invoke(T data, AnalysisContext context) {
            dataList.add(data);
            if (dataList.size() >= batchSize) {
                batchProcessor.accept(new ArrayList<>(dataList));
                dataList.clear();
            }
        }

        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            if (!dataList.isEmpty()) {
                batchProcessor.accept(dataList);
            }
        }

        public List<T> getDataList() {
            return dataList;
        }
    }

    /**
     * 简单数据监听器，用于收集所有数据到列表
     *
     * @param <T> 数据类型
     */
    public static class SimpleDataListener<T> extends AnalysisEventListener<T> {
        private final List<T> dataList;

        public SimpleDataListener(List<T> dataList) {
            this.dataList = dataList;
        }

        @Override
        public void invoke(T data, AnalysisContext context) {
            dataList.add(data);
        }

        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            // 读取完成后的处理逻辑
        }
    }
    
    /**
     * 分页数据监听器，用于分页读取Excel数据
     *
     * @param <T> 数据类型
     */
    public static class PageDataListener<T> extends AnalysisEventListener<T> {
        private final List<T> dataList;
        private final int skipRows;
        private final int pageSize;
        private int currentRow = 0;

        public PageDataListener(List<T> dataList, int skipRows, int pageSize) {
            this.dataList = dataList;
            this.skipRows = skipRows;
            this.pageSize = pageSize;
        }

        @Override
        public void invoke(T data, AnalysisContext context) {
            if (currentRow >= skipRows && dataList.size() < pageSize) {
                dataList.add(data);
            }
            currentRow++;
            
            // 如果已经读取了足够的数据，可以提前结束
            if (dataList.size() >= pageSize) {
                throw new BusinessException("PageReadCompleted");
            }
        }

        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            // 读取完成后的处理逻辑
        }
        
        @Override
        public void onException(Exception exception, AnalysisContext context) throws Exception {
            // 如果是我们自己抛出的异常，表示分页读取完成，不需要继续抛出
            if (exception instanceof BusinessException && "PageReadCompleted".equals(exception.getMessage())) {
                return;
            }
            super.onException(exception, context);
        }
    }
}
